#pragma once
#ifndef CPP11

#include "non_copy_able.h"
#include <list>
#include <string>
#include "functor.h"
#include "buffer.h"
#include "channel.h"
#include "mutex.h"
#include <Windows.h>
#include <SetupAPI.h>
#include <Winternl.h>

namespace el
{
	class EventLoop;
	class SerialPort;

	typedef Function<void(SerialPort*)> SerialMessageCallback;
	typedef NTSTATUS(__stdcall *t_RtlNtStatusToDosError)(
		NTSTATUS Status
		);

	typedef NTSTATUS(__stdcall *t_NtDeviceIoControlFile)(
		IN HANDLE FileHandle,
		IN HANDLE Event OPTIONAL,
		IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
		IN PVOID ApcContext OPTIONAL,
		OUT PIO_STATUS_BLOCK IoStatusBlock,
		IN ULONG IoControlCode,
		IN PVOID InputBuffer OPTIONAL,
		IN ULONG InputBufferLength,
		OUT PVOID OutputBuffer OPTIONAL,
		IN ULONG OutputBufferLength
		);


	class Overlapped : public OVERLAPPED
	{
	public:
		Overlapped(SerialPort* ptr);
		~Overlapped();
		void clear();
		SerialPort* ptr_;
	};

	class SerialPort : public noncopyable
	{
	public:
		enum SerialParity { NONE, ODD, EVEN, MARK, SPACE };
		enum SerialStopBit { STOP_ONE, STOP_ONE5, STOP_TWO };

		SerialPort(EventLoop* loop, const std::string& name);
		~SerialPort();

		std::string portName();
		HANDLE handle();
		DWORD lastError();

		bool open();
		bool close();
		bool config(int baud_rate, int data_bit, SerialParity parity, SerialStopBit stop_bit);

		void setMessageCallback(const SerialMessageCallback& cb);

		void sendDataInLoop(const char* data, int len);

		int readData(char* data, int len);
		int readData(Buffer& buffer);

		bool waitForReadyRead();
		void endAsyncRead();


	private:
		void sendDataInLoop();
		bool listenReadEvent();
		bool startAsyncRead();
		void completeRead(DWORD bytes_transfered);

		static VOID WINAPI apcRoutine(
			PVOID context,
			PIO_STATUS_BLOCK ioStatusBlock,
			DWORD reserved
		);
		static void CALLBACK ioCompletionRoutine(
			DWORD errorCode, DWORD bytesTransfered,
			OVERLAPPED *overlappedBase
		);

		EventLoop* loop_;
		Buffer inbuf_;
		Buffer outbuf_;
		Mutex buf_mutex_;
		Overlapped* read_overlapped_;
		Overlapped* listen_overlapped_;

		HANDLE handle_;
		DCB dcb_;
		DWORD triggered_event_mask = 0;
		std::string name_;

		SerialMessageCallback message_cb_;
		static t_NtDeviceIoControlFile pfnNtDeviceIoControlFile;
		static t_RtlNtStatusToDosError pfnRtlNtStatusToDosError;

		DWORD read_size_;
	};
}
#endif // GT_WINCE
